<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>【并发锁】 相关知识 | 寒冷如铁</title>
    <meta name="generator" content="VuePress 1.8.2">
    <link rel="icon" href="/blog/favicon.ico">
    <script language="javascript" type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script language="javascript" type="text/javascript" src="/blog/js/MouseClickEffect.js"></script>
    <meta name="description" content="好奇就尝试，喜欢就坚持！">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
    
    <link rel="preload" href="/blog/assets/css/0.styles.01ca31a9.css" as="style"><link rel="preload" href="/blog/assets/js/app.0c46c7d8.js" as="script"><link rel="preload" href="/blog/assets/js/3.5679fe00.js" as="script"><link rel="preload" href="/blog/assets/js/1.8cdd2163.js" as="script"><link rel="preload" href="/blog/assets/js/55.3d2e78cc.js" as="script"><link rel="prefetch" href="/blog/assets/js/10.0b6ffd8d.js"><link rel="prefetch" href="/blog/assets/js/11.d27e4c34.js"><link rel="prefetch" href="/blog/assets/js/12.ba5bd991.js"><link rel="prefetch" href="/blog/assets/js/13.ab5bead7.js"><link rel="prefetch" href="/blog/assets/js/14.124a0dc3.js"><link rel="prefetch" href="/blog/assets/js/15.ac521310.js"><link rel="prefetch" href="/blog/assets/js/16.b32290db.js"><link rel="prefetch" href="/blog/assets/js/17.d65c6ccd.js"><link rel="prefetch" href="/blog/assets/js/18.0a7a10ef.js"><link rel="prefetch" href="/blog/assets/js/19.e0e5e91a.js"><link rel="prefetch" href="/blog/assets/js/20.bcc0e382.js"><link rel="prefetch" href="/blog/assets/js/21.59edea58.js"><link rel="prefetch" href="/blog/assets/js/22.8239cecf.js"><link rel="prefetch" href="/blog/assets/js/23.48516c82.js"><link rel="prefetch" href="/blog/assets/js/24.4e1a046f.js"><link rel="prefetch" href="/blog/assets/js/25.56c0a228.js"><link rel="prefetch" href="/blog/assets/js/26.fb764e02.js"><link rel="prefetch" href="/blog/assets/js/27.f96c7435.js"><link rel="prefetch" href="/blog/assets/js/28.31139fa0.js"><link rel="prefetch" href="/blog/assets/js/29.fa31f05f.js"><link rel="prefetch" href="/blog/assets/js/30.49c791b1.js"><link rel="prefetch" href="/blog/assets/js/31.68660a8e.js"><link rel="prefetch" href="/blog/assets/js/32.4e07e3b0.js"><link rel="prefetch" href="/blog/assets/js/33.e08324f8.js"><link rel="prefetch" href="/blog/assets/js/34.9153a266.js"><link rel="prefetch" href="/blog/assets/js/35.49d3bb37.js"><link rel="prefetch" href="/blog/assets/js/36.5a46a47a.js"><link rel="prefetch" href="/blog/assets/js/37.2403de70.js"><link rel="prefetch" href="/blog/assets/js/38.794a3bde.js"><link rel="prefetch" href="/blog/assets/js/39.185570eb.js"><link rel="prefetch" href="/blog/assets/js/4.a5921925.js"><link rel="prefetch" href="/blog/assets/js/40.b5ddc5b2.js"><link rel="prefetch" href="/blog/assets/js/41.81f131ab.js"><link rel="prefetch" href="/blog/assets/js/42.474cb45b.js"><link rel="prefetch" href="/blog/assets/js/43.fe269b94.js"><link rel="prefetch" href="/blog/assets/js/44.123909bf.js"><link rel="prefetch" href="/blog/assets/js/45.85e17df3.js"><link rel="prefetch" href="/blog/assets/js/46.96eb5367.js"><link rel="prefetch" href="/blog/assets/js/47.2c06ceb7.js"><link rel="prefetch" href="/blog/assets/js/48.dcaa5d75.js"><link rel="prefetch" href="/blog/assets/js/49.47475370.js"><link rel="prefetch" href="/blog/assets/js/5.a6bfa893.js"><link rel="prefetch" href="/blog/assets/js/50.29aa41a9.js"><link rel="prefetch" href="/blog/assets/js/51.b049cb19.js"><link rel="prefetch" href="/blog/assets/js/52.9f16a8d1.js"><link rel="prefetch" href="/blog/assets/js/53.9ccedc0d.js"><link rel="prefetch" href="/blog/assets/js/54.c16fde39.js"><link rel="prefetch" href="/blog/assets/js/56.14b1dddb.js"><link rel="prefetch" href="/blog/assets/js/57.a48eb0da.js"><link rel="prefetch" href="/blog/assets/js/58.db985774.js"><link rel="prefetch" href="/blog/assets/js/59.dbb67461.js"><link rel="prefetch" href="/blog/assets/js/6.d5f7873e.js"><link rel="prefetch" href="/blog/assets/js/60.fd9edd5c.js"><link rel="prefetch" href="/blog/assets/js/61.c87eaa37.js"><link rel="prefetch" href="/blog/assets/js/62.09bd4303.js"><link rel="prefetch" href="/blog/assets/js/63.c3a4cb82.js"><link rel="prefetch" href="/blog/assets/js/64.1e623e6b.js"><link rel="prefetch" href="/blog/assets/js/65.5ee98079.js"><link rel="prefetch" href="/blog/assets/js/66.875fdeb3.js"><link rel="prefetch" href="/blog/assets/js/67.52e4feee.js"><link rel="prefetch" href="/blog/assets/js/68.09bb6522.js"><link rel="prefetch" href="/blog/assets/js/69.e5cc1032.js"><link rel="prefetch" href="/blog/assets/js/7.63c57787.js"><link rel="prefetch" href="/blog/assets/js/70.a2307508.js"><link rel="prefetch" href="/blog/assets/js/71.75bc515a.js"><link rel="prefetch" href="/blog/assets/js/72.f7d10a87.js"><link rel="prefetch" href="/blog/assets/js/73.ae777bb5.js"><link rel="prefetch" href="/blog/assets/js/74.ddd90b9a.js"><link rel="prefetch" href="/blog/assets/js/75.5d297adc.js"><link rel="prefetch" href="/blog/assets/js/76.299aaa21.js"><link rel="prefetch" href="/blog/assets/js/77.c93068f7.js"><link rel="prefetch" href="/blog/assets/js/78.727916c9.js"><link rel="prefetch" href="/blog/assets/js/79.605f2aff.js"><link rel="prefetch" href="/blog/assets/js/8.22284503.js"><link rel="prefetch" href="/blog/assets/js/80.0081929d.js"><link rel="prefetch" href="/blog/assets/js/9.8e870e00.js">
    <link rel="stylesheet" href="/blog/assets/css/0.styles.01ca31a9.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container" data-v-19557b78><div data-v-19557b78><div id="loader-wrapper" class="loading-wrapper" data-v-d48f4d20 data-v-19557b78 data-v-19557b78><div class="loader-main" data-v-d48f4d20><div data-v-d48f4d20></div><div data-v-d48f4d20></div><div data-v-d48f4d20></div><div data-v-d48f4d20></div></div> <!----> <!----></div> <div class="password-shadow password-wrapper-out" style="display:none;" data-v-64685f0e data-v-19557b78 data-v-19557b78><h3 class="title" style="display:none;" data-v-64685f0e data-v-64685f0e>寒冷如铁</h3> <!----> <label id="box" class="inputBox" style="display:none;" data-v-64685f0e data-v-64685f0e><input type="password" value="" data-v-64685f0e> <span data-v-64685f0e>Konck! Knock!</span> <button data-v-64685f0e>OK</button></label> <div class="footer" style="display:none;" data-v-64685f0e data-v-64685f0e><span data-v-64685f0e><i class="iconfont reco-theme" data-v-64685f0e></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-64685f0e>vuePress-theme-reco</a></span> <span data-v-64685f0e><i class="iconfont reco-copyright" data-v-64685f0e></i> <a data-v-64685f0e><span data-v-64685f0e>寒铁</span>
            
          <span data-v-64685f0e>2020 - </span>
          2022
        </a></span></div></div> <div class="hide" data-v-19557b78><header class="navbar" data-v-19557b78><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/blog/" class="home-link router-link-active"><!----> <span class="site-name">寒冷如铁</span></a> <div class="links"><div class="color-picker"><a class="color-button"><i class="iconfont reco-color"></i></a> <div class="color-picker-menu" style="display:none;"><div class="mode-options"><h4 class="title">Choose mode</h4> <ul class="color-mode-options"><li class="dark">dark</li><li class="auto active">auto</li><li class="light">light</li></ul></div></div></div> <div class="search-box"><i class="iconfont reco-search"></i> <input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/blog/" class="nav-link"><i class="iconfont reco-home"></i>
  首页
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-category"></i>
      分类
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/categories/build/" class="nav-link"><i class="iconfont undefined"></i>
  build
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/db/" class="nav-link"><i class="iconfont undefined"></i>
  db
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/docker/" class="nav-link"><i class="iconfont undefined"></i>
  docker
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/workflow/" class="nav-link"><i class="iconfont undefined"></i>
  workflow
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/git/" class="nav-link"><i class="iconfont undefined"></i>
  git
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/java/" class="nav-link"><i class="iconfont undefined"></i>
  java
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/source/" class="nav-link"><i class="iconfont undefined"></i>
  source
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/linux/" class="nav-link"><i class="iconfont undefined"></i>
  linux
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/network/" class="nav-link"><i class="iconfont undefined"></i>
  network
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/python/" class="nav-link"><i class="iconfont undefined"></i>
  python
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/project/" class="nav-link"><i class="iconfont undefined"></i>
  project
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/share/" class="nav-link"><i class="iconfont undefined"></i>
  share
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/software/" class="nav-link"><i class="iconfont undefined"></i>
  software
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/spring/" class="nav-link"><i class="iconfont undefined"></i>
  spring
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/tomcat/" class="nav-link"><i class="iconfont undefined"></i>
  tomcat
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/web/" class="nav-link"><i class="iconfont undefined"></i>
  web
</a></li></ul></div></div><div class="nav-item"><a href="/blog/tag/" class="nav-link"><i class="iconfont reco-tag"></i>
  标签
</a></div><div class="nav-item"><a href="/blog/views/guide.html" class="nav-link"><i class="iconfont reco-other"></i>
  记录
</a></div><div class="nav-item"><a href="/blog/timeline/" class="nav-link"><i class="iconfont reco-date"></i>
  时间线
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-message"></i>
      外链
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="https://github.com/huhuhan" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-github"></i>
  GitHub
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://gitee.com/huhu_han" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  Gitee
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://console.leancloud.app/#/apps" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  有道云笔记
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://console.leancloud.app/#/apps" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  LeadCloud
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://cloud.seafile.com/" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  Seafile
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://huhu_han.gitee.io/downgit" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  DownGit
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div> <!----></nav></div></header> <div class="sidebar-mask" data-v-19557b78></div> <aside class="sidebar" data-v-19557b78><div class="personal-info-wrapper" data-v-b038cec6><img src="/blog/avatar.png" alt="author-avatar" class="personal-img" data-v-b038cec6> <h3 class="name" data-v-b038cec6>
    寒铁
  </h3> <div class="num" data-v-b038cec6><div data-v-b038cec6><h3 data-v-b038cec6>66</h3> <h6 data-v-b038cec6>文章</h6></div> <div data-v-b038cec6><h3 data-v-b038cec6>17</h3> <h6 data-v-b038cec6>标签</h6></div></div> <hr data-v-b038cec6></div> <nav class="nav-links"><div class="nav-item"><a href="/blog/" class="nav-link"><i class="iconfont reco-home"></i>
  首页
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-category"></i>
      分类
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/categories/build/" class="nav-link"><i class="iconfont undefined"></i>
  build
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/db/" class="nav-link"><i class="iconfont undefined"></i>
  db
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/docker/" class="nav-link"><i class="iconfont undefined"></i>
  docker
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/workflow/" class="nav-link"><i class="iconfont undefined"></i>
  workflow
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/git/" class="nav-link"><i class="iconfont undefined"></i>
  git
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/java/" class="nav-link"><i class="iconfont undefined"></i>
  java
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/source/" class="nav-link"><i class="iconfont undefined"></i>
  source
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/linux/" class="nav-link"><i class="iconfont undefined"></i>
  linux
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/network/" class="nav-link"><i class="iconfont undefined"></i>
  network
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/python/" class="nav-link"><i class="iconfont undefined"></i>
  python
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/project/" class="nav-link"><i class="iconfont undefined"></i>
  project
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/share/" class="nav-link"><i class="iconfont undefined"></i>
  share
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/software/" class="nav-link"><i class="iconfont undefined"></i>
  software
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/spring/" class="nav-link"><i class="iconfont undefined"></i>
  spring
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/tomcat/" class="nav-link"><i class="iconfont undefined"></i>
  tomcat
</a></li><li class="dropdown-item"><!----> <a href="/blog/categories/web/" class="nav-link"><i class="iconfont undefined"></i>
  web
</a></li></ul></div></div><div class="nav-item"><a href="/blog/tag/" class="nav-link"><i class="iconfont reco-tag"></i>
  标签
</a></div><div class="nav-item"><a href="/blog/views/guide.html" class="nav-link"><i class="iconfont reco-other"></i>
  记录
</a></div><div class="nav-item"><a href="/blog/timeline/" class="nav-link"><i class="iconfont reco-date"></i>
  时间线
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-message"></i>
      外链
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="https://github.com/huhuhan" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-github"></i>
  GitHub
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://gitee.com/huhu_han" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  Gitee
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://console.leancloud.app/#/apps" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  有道云笔记
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://console.leancloud.app/#/apps" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  LeadCloud
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://cloud.seafile.com/" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  Seafile
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://huhu_han.gitee.io/downgit" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-other"></i>
  DownGit
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div> <!----></nav>  <ul class="sidebar-links"><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>【并发锁】 相关知识</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/blog/views/share/concurrent-lock.html#并发锁" class="sidebar-link">并发锁</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#可重入锁" class="sidebar-link">可重入锁</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#公平锁与非公平锁" class="sidebar-link">公平锁与非公平锁</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#独享锁与共享锁" class="sidebar-link">独享锁与共享锁</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#悲观锁与乐观锁" class="sidebar-link">悲观锁与乐观锁</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#偏向锁、轻量级锁、重量级锁" class="sidebar-link">偏向锁、轻量级锁、重量级锁</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#分段锁" class="sidebar-link">分段锁</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#显示锁和内置锁" class="sidebar-link">显示锁和内置锁</a></li></ul></li><li><a href="/blog/views/share/concurrent-lock.html#lock-和-condition" class="sidebar-link">Lock 和 Condition</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#为何引入" class="sidebar-link">为何引入</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#lock-接口" class="sidebar-link">Lock 接口</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#condition" class="sidebar-link">Condition</a></li></ul></li><li><a href="/blog/views/share/concurrent-lock.html#reentrantlock" class="sidebar-link">ReentrantLock</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#reentrantlock-的特性" class="sidebar-link">ReentrantLock 的特性</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#reentrantlock-的用法" class="sidebar-link">ReentrantLock 的用法</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#reentrantlock-的原理" class="sidebar-link">ReentrantLock 的原理</a></li></ul></li><li><a href="/blog/views/share/concurrent-lock.html#reentrantreadwritelock" class="sidebar-link">ReentrantReadWriteLock</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#reentrantreadwritelock-的特性" class="sidebar-link">ReentrantReadWriteLock 的特性</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#reentrantreadwritelock-的用法" class="sidebar-link">ReentrantReadWriteLock 的用法</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#reentrantreadwritelock-的原理" class="sidebar-link">ReentrantReadWriteLock 的原理</a></li></ul></li><li><a href="/blog/views/share/concurrent-lock.html#stampedlock" class="sidebar-link">StampedLock</a><ul class="sidebar-sub-headers"></ul></li><li><a href="/blog/views/share/concurrent-lock.html#aqs" class="sidebar-link">AQS</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#aqs-的要点" class="sidebar-link">AQS 的要点</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#aqs-的应用" class="sidebar-link">AQS 的应用</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#aqs-的原理" class="sidebar-link">AQS 的原理</a></li></ul></li><li><a href="/blog/views/share/concurrent-lock.html#死锁" class="sidebar-link">死锁</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#什么是死锁" class="sidebar-link">什么是死锁</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#如何定位死锁" class="sidebar-link">如何定位死锁</a></li><li class="sidebar-sub-header"><a href="/blog/views/share/concurrent-lock.html#如何避免死锁" class="sidebar-link">如何避免死锁</a></li></ul></li></ul></section></li></ul> </aside> <div class="password-shadow password-wrapper-in" style="display:none;" data-v-64685f0e data-v-19557b78><h3 class="title" style="display:none;" data-v-64685f0e data-v-64685f0e>【并发锁】 相关知识</h3> <!----> <label id="box" class="inputBox" style="display:none;" data-v-64685f0e data-v-64685f0e><input type="password" value="" data-v-64685f0e> <span data-v-64685f0e>Konck! Knock!</span> <button data-v-64685f0e>OK</button></label> <div class="footer" style="display:none;" data-v-64685f0e data-v-64685f0e><span data-v-64685f0e><i class="iconfont reco-theme" data-v-64685f0e></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-64685f0e>vuePress-theme-reco</a></span> <span data-v-64685f0e><i class="iconfont reco-copyright" data-v-64685f0e></i> <a data-v-64685f0e><span data-v-64685f0e>寒铁</span>
            
          <span data-v-64685f0e>2020 - </span>
          2022
        </a></span></div></div> <div data-v-19557b78><main class="page"><div class="page-title" style="display:none;"><h1>【并发锁】 相关知识</h1> <hr> <div data-v-484a899e><i class="iconfont reco-account" data-v-484a899e><span data-v-484a899e>java宝典、寒铁</span></i> <i class="iconfont reco-date" data-v-484a899e><span data-v-484a899e>2021-02-18</span></i> <i class="iconfont reco-eye" data-v-484a899e><span id="/blog/views/share/concurrent-lock.html" data-flag-title="Your Article Title" class="leancloud-visitors" data-v-484a899e><a class="leancloud-visitors-count" style="font-size:.9rem;font-weight:normal;color:#999;"></a></span></i> <i class="iconfont reco-tag tags" data-v-484a899e><span class="tag-item" data-v-484a899e>
      java
    </span><span class="tag-item" data-v-484a899e>
      share
    </span></i></div></div> <div class="theme-reco-content content__default" style="display:none;"><div class="custom-block warning"><p>原文：<a href="https://my.oschina.net/u/4569968/blog/4703350" target="_blank" rel="noopener noreferrer">深入理解 Java 并发锁<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></div> <h2 id="并发锁"><a href="#并发锁" class="header-anchor">#</a> 并发锁</h2> <p>确保线程安全最常见的做法是利用锁机制（<code>Lock</code>、<code>sychronized</code>）来对共享数据做互斥同步，这样在同一个时刻，只有一个线程可以执行某个方法或者某个代码块，那么操作必然是<strong>原子性的，线程安全的</strong>。</p> <h3 id="可重入锁"><a href="#可重入锁" class="header-anchor">#</a> 可重入锁</h3> <p><strong>可重入锁，顾名思义，指的是线程可以重复获取同一把锁</strong>。即同一个线程在外层方法获取了锁，在进入内层方法会自动获取锁。</p> <p><strong>可重入锁可以在一定程度上避免死锁</strong>。</p> <ul><li><strong>ReentrantLock 、ReentrantReadWriteLock 是可重入锁</strong>。这点，从其命名也不难看出。</li> <li><strong>synchronized 也是一个可重入锁</strong>。</li></ul> <details class="custom-block details"><summary>【示例】synchronized 的可重入示例</summary> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">synchronized</span> <span class="token keyword">void</span> <span class="token function">setA</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span><span class="token punctuation">{</span>
    <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">setB</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">synchronized</span> <span class="token keyword">void</span> <span class="token function">setB</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span><span class="token punctuation">{</span>
    <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面的代码就是一个典型场景：如果使用的锁不是可重入锁的话，<code>setB</code> 可能不会被当前线程执行，从而造成死锁。</p></details> <details class="custom-block details"><summary>【示例】ReentrantLock 的可重入示例</summary> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">class</span> <span class="token class-name">Task</span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token keyword">int</span> value<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Lock</span> lock <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReentrantLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token class-name">Task</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 获取锁</span>
        lock<span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span> value<span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            <span class="token comment">// 保证锁能释放</span>
            lock<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">addOne</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 获取锁</span>
        lock<span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token comment">// 注意：此处已经成功获取锁，进入 get 方法后，又尝试获取锁，</span>
            <span class="token comment">// 如果锁不是可重入的，会导致死锁</span>
            value <span class="token operator">=</span> <span class="token number">1</span> <span class="token operator">+</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            <span class="token comment">// 保证锁能释放</span>
            lock<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br></div></div></details> <h3 id="公平锁与非公平锁"><a href="#公平锁与非公平锁" class="header-anchor">#</a> 公平锁与非公平锁</h3> <ul><li><strong>公平锁</strong> - 公平锁是指 <strong>多线程按照申请锁的顺序来获取锁</strong>。</li> <li><strong>非公平锁</strong> - 非公平锁是指 <strong>多线程不按照申请锁的顺序来获取锁</strong> 。这就可能会出现优先级反转（后来者居上）或者饥饿现象（某线程总是抢不过别的线程，导致始终无法执行）。</li></ul> <p>公平锁为了保证线程申请顺序，势必要付出一定的性能代价，因此其吞吐量一般低于非公平锁。</p> <p>公平锁与非公平锁 在 Java 中的典型实现：</p> <ul><li><strong>synchronized 只支持非公平锁</strong>。</li> <li><strong>ReentrantLock 、ReentrantReadWriteLock，默认是非公平锁，但支持公平锁</strong>。</li></ul> <h3 id="独享锁与共享锁"><a href="#独享锁与共享锁" class="header-anchor">#</a> 独享锁与共享锁</h3> <p>独享锁与共享锁是一种广义上的说法，从实际用途上来看，也常被称为互斥锁与读写锁。</p> <ul><li><strong>独享锁</strong> - 独享锁是指 <strong>锁一次只能被一个线程所持有</strong>。</li> <li><strong>共享锁</strong> - 共享锁是指 <strong>锁可被多个线程所持有</strong>。</li></ul> <p>独享锁与共享锁在 Java 中的典型实现：</p> <ul><li><strong>synchronized 、ReentrantLock 只支持独享锁</strong>。</li> <li><strong>ReentrantReadWriteLock 其写锁是独享锁，其读锁是共享锁</strong>。读锁是共享锁使得并发读是非常高效的，读写，写读 ，写写的过程是互斥的。</li></ul> <h3 id="悲观锁与乐观锁"><a href="#悲观锁与乐观锁" class="header-anchor">#</a> 悲观锁与乐观锁</h3> <p>乐观锁与悲观锁不是指具体的什么类型的锁，而是<strong>处理并发同步的策略</strong>。</p> <ul><li><strong>悲观锁</strong> - 悲观锁对于并发采取悲观的态度，认为：<strong>不加锁的并发操作一定会出问题</strong>。<strong>悲观锁适合写操作频繁的场景</strong>。</li> <li><strong>乐观锁</strong> - 乐观锁对于并发采取乐观的态度，认为：<strong>不加锁的并发操作也没什么问题。对于同一个数据的并发操作，是不会发生修改的</strong>。在更新数据的时候，会采用不断尝试更新的方式更新数据。<strong>乐观锁适合读多写少的场景</strong>。</li></ul> <p>悲观锁与乐观锁在 Java 中的典型实现：</p> <ul><li>悲观锁在 Java 中的应用就是通过使用 <code>synchronized</code> 和 <code>Lock</code> 显示加锁来进行互斥同步，这是一种阻塞同步。</li> <li>乐观锁在 Java 中的应用就是采用 <code>CAS</code> 机制（<code>CAS</code> 操作通过 <code>Unsafe</code> 类提供，但这个类不直接暴露为 API，所以都是间接使用，如各种原子类）。</li></ul> <h3 id="偏向锁、轻量级锁、重量级锁"><a href="#偏向锁、轻量级锁、重量级锁" class="header-anchor">#</a> 偏向锁、轻量级锁、重量级锁</h3> <p>所谓轻量级锁与重量级锁，指的是<strong>锁控制粒度的粗细</strong>。显然，控制粒度越细，阻塞开销越小，并发性也就越高。</p> <p>Java 1.6 以前，重量级锁一般指的是 <code>synchronized</code> ，而轻量级锁指的是 <code>volatile</code>。</p> <p>Java 1.6 以后，针对 <code>synchronized</code> 做了大量优化，引入 <strong>4 种锁状态</strong>： 无锁状态、偏向锁、轻量级锁和重量级锁。锁可以单向的从偏向锁升级到轻量级锁，再从轻量级锁升级到重量级锁 。</p> <ul><li><strong>偏向锁</strong> - 偏向锁是指一段同步代码一直被一个线程所访问，那么该线程会自动获取锁。降低获取锁的代价。</li> <li><strong>轻量级锁</strong> - 是指当锁是偏向锁的时候，被另一个线程所访问，偏向锁就会升级为轻量级锁，其他线程会通过自旋的形式尝试获取锁，不会阻塞，提高性能。</li> <li><strong>重量级锁</strong> - 是指当锁为轻量级锁的时候，另一个线程虽然是自旋，但自旋不会一直持续下去，当自旋一定次数的时候，还没有获取到锁，就会进入阻塞，该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞，性能降低。</li></ul> <h3 id="分段锁"><a href="#分段锁" class="header-anchor">#</a> 分段锁</h3> <p>分段锁其实是一种锁的设计，并不是具体的一种锁。所谓分段锁，就是把锁的对象分成多段，每段独立控制，使得锁粒度更细，减少阻塞开销，从而提高并发性。这其实很好理解，就像高速公路上的收费站，如果只有一个收费口，那所有的车只能排成一条队缴费；如果有多个收费口，就可以分流了。</p> <p><code>Hashtable</code> 使用 <code>synchronized</code> 修饰方法来保证线程安全性，那么面对线程的访问，Hashtable 就会锁住整个对象，所有的其它线程只能等待，这种阻塞方式的吞吐量显然很低。</p> <p>Java 1.7 以前的 <code>ConcurrentHashMap</code> 就是分段锁的典型案例。<code>ConcurrentHashMap</code> 维护了一个 <code>Segment</code> 数组，一般称为分段桶。</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">final</span> <span class="token class-name">Segment</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">K</span><span class="token punctuation">,</span><span class="token class-name">V</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">[</span><span class="token punctuation">]</span> segments<span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>当有线程访问 <code>ConcurrentHashMap</code> 的数据时，<code>ConcurrentHashMap</code> 会先根据 hashCode 计算出数据在哪个桶（即哪个 Segment），然后锁住这个 <code>Segment</code>。</p> <h3 id="显示锁和内置锁"><a href="#显示锁和内置锁" class="header-anchor">#</a> 显示锁和内置锁</h3> <p>Java 1.5 之前，协调对共享对象的访问时可以使用的机制只有 <code>synchronized</code> 和 <code>volatile</code>。这两个都属于内置锁，即锁的申请和释放都是由 JVM 所控制。</p> <p>Java 1.5 之后，增加了新的机制：<code>ReentrantLock</code>、<code>ReentrantReadWriteLock</code> ，这类锁的申请和释放都可以由程序所控制，所以常被称为显示锁。</p> <blockquote><p>注意：如果不需要 <code>ReentrantLock</code>、<code>ReentrantReadWriteLock</code> 所提供的高级同步特性，<strong>应该优先考虑使用 synchronized</strong> 。理由如下：</p> <ul><li>Java 1.6 以后，<code>synchronized</code> 做了大量的优化，其性能已经与 <code>ReentrantLock</code>、<code>ReentrantReadWriteLock</code> 基本上持平。</li> <li>从趋势来看，Java 未来更可能会优化 <code>synchronized</code> ，而不是 <code>ReentrantLock</code>、<code>ReentrantReadWriteLock</code> ，因为 <code>synchronized</code> 是 JVM 内置属性，它能执行一些优化。</li> <li><code>ReentrantLock</code>、<code>ReentrantReadWriteLock</code> 申请和释放锁都是由程序控制，如果使用不当，可能造成死锁，这是很危险的。</li></ul></blockquote> <p>以下对比一下显示锁和内置锁的差异：</p> <ul><li>主动获取锁和释放锁
<ul><li><code>synchronized</code> 不能主动获取锁和释放锁。获取锁和释放锁都是 JVM 控制的。</li> <li><code>ReentrantLock</code> 可以主动获取锁和释放锁。（如果忘记释放锁，就可能产生死锁）。</li></ul></li> <li>响应中断
<ul><li><code>synchronized</code> 不能响应中断。</li> <li><code>ReentrantLock</code> 可以响应中断。</li></ul></li> <li>超时机制
<ul><li><code>synchronized</code> 没有超时机制。</li> <li><code>ReentrantLock</code> 有超时机制。<code>ReentrantLock</code> 可以设置超时时间，超时后自动释放锁，避免一直等待。</li></ul></li> <li>支持公平锁
<ul><li><code>synchronized</code> 只支持非公平锁。</li> <li><code>ReentrantLock</code> 支持非公平锁和公平锁。</li></ul></li> <li>是否支持共享
<ul><li>被 <code>synchronized</code> 修饰的方法或代码块，只能被一个线程访问（独享）。如果这个线程被阻塞，其他线程也只能等待</li> <li><code>ReentrantLock</code> 可以基于 <code>Condition</code> 灵活的控制同步条件。</li></ul></li> <li>是否支持读写分离
<ul><li><code>synchronized</code> 不支持读写锁分离；</li> <li><code>ReentrantReadWriteLock</code> 支持读写锁，从而使阻塞读写的操作分开，有效提高并发性。</li></ul></li></ul> <h2 id="lock-和-condition"><a href="#lock-和-condition" class="header-anchor">#</a> Lock 和 Condition</h2> <h3 id="为何引入"><a href="#为何引入" class="header-anchor">#</a> 为何引入</h3> <p>并发编程领域，有两大核心问题：一个是<strong>互斥</strong>，即同一时刻只允许一个线程访问共享资源；另一个是<strong>同步</strong>，即线程之间如何通信、协作。这两大问题，管程都是能够解决的。<strong>Java SDK 并发包通过 Lock 和 Condition 两个接口来实现管程，其中 Lock 用于解决互斥问题，Condition 用于解决同步问题</strong>。</p> <p>synchronized 是管程的一种实现，既然如此，何必再提供 Lock 和 Condition。</p> <p>JDK 1.6 以前，synchronized 还没有做优化，性能远低于 Lock。但是，性能不是引入 Lock 的最重要因素。真正关键在于：synchronized 使用不当，可能会出现死锁。</p> <p>synchronized 无法通过<strong>破坏不可抢占条件</strong>来避免死锁。原因是 synchronized 申请资源的时候，如果申请不到，线程直接进入阻塞状态了，而线程进入阻塞状态，啥都干不了，也释放不了线程已经占有的资源。</p> <p>与内置锁 <code>synchronized</code> 不同的是，<strong>Lock 提供了一组无条件的、可轮询的、定时的以及可中断的锁操作</strong>，所有获取锁、释放锁的操作都是显式的操作。</p> <ul><li><strong>能够响应中断</strong>。synchronized 的问题是，持有锁 A 后，如果尝试获取锁 B 失败，那么线程就进入阻塞状态，一旦发生死锁，就没有任何机会来唤醒阻塞的线程。但如果阻塞状态的线程能够响应中断信号，也就是说当我们给阻塞的线程发送中断信号的时候，能够唤醒它，那它就有机会释放曾经持有的锁 A。这样就破坏了不可抢占条件了。</li> <li><strong>支持超时</strong>。如果线程在一段时间之内没有获取到锁，不是进入阻塞状态，而是返回一个错误，那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。</li> <li><strong>非阻塞地获取锁</strong>。如果尝试获取锁失败，并不进入阻塞状态，而是直接返回，那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。</li></ul> <h3 id="lock-接口"><a href="#lock-接口" class="header-anchor">#</a> Lock 接口</h3> <p><code>Lock</code> 的接口定义如下：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Lock</span> <span class="token punctuation">{</span>
    <span class="token keyword">void</span> <span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">void</span> <span class="token function">lockInterruptibly</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">;</span>
    <span class="token keyword">boolean</span> <span class="token function">tryLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">boolean</span> <span class="token function">tryLock</span><span class="token punctuation">(</span><span class="token keyword">long</span> time<span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span> unit<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">;</span>
    <span class="token keyword">void</span> <span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">Condition</span> <span class="token function">newCondition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><ul><li><code>lock()</code> - 获取锁。</li> <li><code>unlock()</code> - 释放锁。</li> <li><code>tryLock()</code> - 尝试获取锁，仅在调用时锁未被另一个线程持有的情况下，才获取该锁。</li> <li><code>tryLock(long time, TimeUnit unit)</code> - 和 <code>tryLock()</code> 类似，区别仅在于限定时间，如果限定时间内未获取到锁，视为失败。</li> <li><code>lockInterruptibly()</code> - 锁未被另一个线程持有，且线程没有被中断的情况下，才能获取锁。</li> <li><code>newCondition()</code> - 返回一个绑定到 <code>Lock</code> 对象上的 <code>Condition</code> 实例。</li></ul> <h3 id="condition"><a href="#condition" class="header-anchor">#</a> Condition</h3> <p><strong>Condition 实现了管程模型里面的条件变量</strong></p> <p>前文中提过 <code>Lock</code> 接口中 有一个 <code>newCondition()</code> 方法用于返回一个绑定到 <code>Lock</code> 对象上的 <code>Condition</code> 实例。<code>Condition</code> 是什么？有什么作用？本节将一一讲解。</p> <p>在单线程中，一段代码的执行可能依赖于某个状态，如果不满足状态条件，代码就不会被执行（典型的场景，如：<code>if ... else ...</code>）。在并发环境中，当一个线程判断某个状态条件时，其状态可能是由于其他线程的操作而改变，这时就需要有一定的协调机制来确保在同一时刻，数据只能被一个线程锁修改，且修改的数据状态被所有线程所感知。</p> <p>Java 1.5 之前，主要是利用 <code>Object</code> 类中的 <code>wait</code>、<code>notify</code>、<code>notifyAll</code> 配合 <code>synchronized</code> 来进行线程间通信 。</p> <p><code>wait</code>、<code>notify</code>、<code>notifyAll</code> 需要配合 <code>synchronized</code> 使用，不适用于 <code>Lock</code>。而使用 <code>Lock</code> 的线程，彼此间通信应该使用 <code>Condition</code> 。这可以理解为，什么样的锁配什么样的钥匙。</p> <ul><li><strong>内置锁（synchronized）配合内置条件队列（wait、notify、notifyAll ）</strong></li> <li><strong>显式锁（Lock）配合显式条件队列（Condition ）</strong>。</li></ul> <p><strong>Condition 的特性</strong></p> <p><code>Condition</code> 接口定义如下：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Condition</span> <span class="token punctuation">{</span>
    <span class="token keyword">void</span> <span class="token function">await</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">;</span>
    <span class="token keyword">void</span> <span class="token function">awaitUninterruptibly</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">long</span> <span class="token function">awaitNanos</span><span class="token punctuation">(</span><span class="token keyword">long</span> nanosTimeout<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">;</span>
    <span class="token keyword">boolean</span> <span class="token function">await</span><span class="token punctuation">(</span><span class="token keyword">long</span> time<span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span> unit<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">;</span>
    <span class="token keyword">boolean</span> <span class="token function">awaitUntil</span><span class="token punctuation">(</span><span class="token class-name">Date</span> deadline<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">;</span>
    <span class="token keyword">void</span> <span class="token function">signal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">void</span> <span class="token function">signalAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>其中，<code>await</code>、<code>signal</code>、<code>signalAll</code> 与 <code>wait</code>、<code>notify</code>、<code>notifyAll</code> 相对应，功能也相似。除此以外，<code>Condition</code> 相比内置条件队列（ <code>wait</code>、<code>notify</code>、<code>notifyAll</code> ），提供了更为丰富的功能：</p> <ul><li>每个锁（<code>Lock</code>）上可以存在多个 <code>Condition</code>，这意味着锁的状态条件可以有多个。</li> <li>支持公平的或非公平的队列操作。</li> <li>支持可中断的条件等待，相关方法：<code>awaitUninterruptibly()</code> 。</li> <li>支持可定时的等待，相关方法：<code>awaitNanos(long)</code> 、<code>await(long, TimeUnit)</code>、<code>awaitUntil(Date)</code>。</li></ul> <p><strong>Condition 的用法</strong></p> <details class="custom-block details"><summary>【示例】以 Condition 来实现一个消费者、生产者模式。</summary> <p>产品类</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">class</span> <span class="token class-name">Message</span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Lock</span> lock <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReentrantLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Condition</span> producedMsg <span class="token operator">=</span> lock<span class="token punctuation">.</span><span class="token function">newCondition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Condition</span> consumedMsg <span class="token operator">=</span> lock<span class="token punctuation">.</span><span class="token function">newCondition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token class-name">String</span> message<span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">boolean</span> state<span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">boolean</span> end<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">consume</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">//lock</span>
        lock<span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token comment">// no new message wait for new message</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>state<span class="token punctuation">)</span> <span class="token punctuation">{</span> producedMsg<span class="token punctuation">.</span><span class="token function">await</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;consume message : &quot;</span> <span class="token operator">+</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
            state <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
            <span class="token comment">// message consumed, notify waiting thread</span>
            consumedMsg<span class="token punctuation">.</span><span class="token function">signal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> ie<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;Thread interrupted - viewMessage&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            lock<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">produce</span><span class="token punctuation">(</span><span class="token class-name">String</span> message<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        lock<span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token comment">// last message not consumed, wait for it be consumed</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token punctuation">{</span> consumedMsg<span class="token punctuation">.</span><span class="token function">await</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;produce msg: &quot;</span> <span class="token operator">+</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> message<span class="token punctuation">;</span>
            state <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
            <span class="token comment">// new message added, notify waiting thread</span>
            producedMsg<span class="token punctuation">.</span><span class="token function">signal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> ie<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;Thread interrupted - publishMessage&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            lock<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">isEnd</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> end<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setEnd</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> end<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>end <span class="token operator">=</span> end<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br></div></div><p>消费者</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">class</span> <span class="token class-name">MessageConsumer</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token class-name">Message</span> message<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token class-name">MessageConsumer</span><span class="token punctuation">(</span><span class="token class-name">Message</span> msg<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        message <span class="token operator">=</span> msg<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>message<span class="token punctuation">.</span><span class="token function">isEnd</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> message<span class="token punctuation">.</span><span class="token function">consume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><p>生产者</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">class</span> <span class="token class-name">MessageProducer</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token class-name">Message</span> message<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token class-name">MessageProducer</span><span class="token punctuation">(</span><span class="token class-name">Message</span> msg<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        message <span class="token operator">=</span> msg<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">produce</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">produce</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">&gt;</span></span> msgs <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        msgs<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">&quot;Begin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        msgs<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">&quot;Msg1&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        msgs<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">&quot;Msg2&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> msg <span class="token operator">:</span> msgs<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            message<span class="token punctuation">.</span><span class="token function">produce</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">try</span> <span class="token punctuation">{</span>
                <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

        message<span class="token punctuation">.</span><span class="token function">produce</span><span class="token punctuation">(</span><span class="token string">&quot;End&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        message<span class="token punctuation">.</span><span class="token function">setEnd</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br></div></div><p>测试</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">LockConditionDemo</span> <span class="token punctuation">{</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">Message</span> msg <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Message</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Thread</span> producer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">MessageProducer</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Thread</span> consumer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">MessageConsumer</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        producer<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        consumer<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div></details> <h2 id="reentrantlock"><a href="#reentrantlock" class="header-anchor">#</a> ReentrantLock</h2> <p><code>ReentrantLock</code> 类是 <code>Lock</code> 接口的具体实现，与内置锁 <code>synchronized</code> 相同的是，它是一个<strong>可重入锁</strong>。</p> <h3 id="reentrantlock-的特性"><a href="#reentrantlock-的特性" class="header-anchor">#</a> ReentrantLock 的特性</h3> <p><code>ReentrantLock</code> 的特性如下：</p> <ul><li><strong>ReentrantLock 提供了与 synchronized 相同的互斥性、内存可见性和可重入性</strong>。</li> <li><code>ReentrantLock</code> <strong>支持公平锁和非公平锁</strong>（默认）两种模式。</li> <li><code>ReentrantLock</code> 实现了 <code>Lock</code> 接口，支持了 <code>synchronized</code>所不具备的<strong>灵活性</strong>。
<ul><li><code>synchronized</code> 无法中断一个正在等待获取锁的线程</li> <li><code>synchronized</code> 无法在请求获取一个锁时无休止地等待</li></ul></li></ul> <h3 id="reentrantlock-的用法"><a href="#reentrantlock-的用法" class="header-anchor">#</a> ReentrantLock 的用法</h3> <p><strong>ReentrantLock 的构造方法</strong></p> <p><code>ReentrantLock</code> 有两个构造方法：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token class-name">ReentrantLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token class-name">ReentrantLock</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> fair<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><ul><li><code>ReentrantLock()</code>   默认构造方法会初始化一个<strong>非公平锁（NonfairSync）</strong>；</li> <li><code>ReentrantLock(boolean)</code> <code>new ReentrantLock(true)</code> 会初始化一个<strong>公平锁（FairSync）</strong>。</li></ul> <p><strong>lock 和 unlock 方法</strong></p> <ul><li><code>lock()</code> <strong>无条件获取锁</strong>。如果当前线程无法获取锁，则当前线程进入休眠状态不可用，直至当前线程获取到锁。如果该锁没有被另一个线程持有，则获取该锁并立即返回，将锁的持有计数设置为 1。</li> <li><code>unlock()</code>  用于<strong>释放锁</strong>。</li></ul> <blockquote><p>🔔 注意：请务必牢记，获取锁操作 <strong>lock() 必须在 try catch 块外进行，并且将释放锁操作 unlock() 放在 finally 块中进行，以保证锁一定被被释放，防止死锁的发生</strong>。</p></blockquote> <details class="custom-block details"><summary>【示例】ReentrantLock 的基本操作</summary> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ReentrantLockDemo</span> <span class="token punctuation">{</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">Task</span> task <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Task</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">MyThread</span> tA <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token string">&quot;Thread-A&quot;</span><span class="token punctuation">,</span> task<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">MyThread</span> tB <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token string">&quot;Thread-B&quot;</span><span class="token punctuation">,</span> task<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">MyThread</span> tC <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token string">&quot;Thread-C&quot;</span><span class="token punctuation">,</span> task<span class="token punctuation">)</span><span class="token punctuation">;</span>
        tA<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        tB<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        tC<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">MyThread</span> <span class="token keyword">extends</span> <span class="token class-name">Thread</span> <span class="token punctuation">{</span>

        <span class="token keyword">private</span> <span class="token class-name">Task</span> task<span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token class-name">Task</span> task<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">super</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>task <span class="token operator">=</span> task<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token annotation punctuation">@Override</span>
        <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            task<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

    <span class="token punctuation">}</span>

    <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Task</span> <span class="token punctuation">{</span>

        <span class="token keyword">private</span> <span class="token class-name">ReentrantLock</span> lock <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReentrantLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            lock<span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">try</span> <span class="token punctuation">{</span>
                <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">3</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>lock<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

                    <span class="token comment">// 查询当前线程 hold 住此锁的次数</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;\t holdCount: &quot;</span> <span class="token operator">+</span> lock<span class="token punctuation">.</span><span class="token function">getHoldCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

                    <span class="token comment">// 查询正等待获取此锁的线程数</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;\t queuedLength: &quot;</span> <span class="token operator">+</span> lock<span class="token punctuation">.</span><span class="token function">getQueueLength</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

                    <span class="token comment">// 是否为公平锁</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;\t isFair: &quot;</span> <span class="token operator">+</span> lock<span class="token punctuation">.</span><span class="token function">isFair</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

                    <span class="token comment">// 是否被锁住</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;\t isLocked: &quot;</span> <span class="token operator">+</span> lock<span class="token punctuation">.</span><span class="token function">isLocked</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

                    <span class="token comment">// 是否被当前线程持有锁</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;\t isHeldByCurrentThread: &quot;</span> <span class="token operator">+</span> lock<span class="token punctuation">.</span><span class="token function">isHeldByCurrentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

                    <span class="token keyword">try</span> <span class="token punctuation">{</span>
                        <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">}</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
                lock<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br><span class="line-number">64</span><br><span class="line-number">65</span><br><span class="line-number">66</span><br><span class="line-number">67</span><br></div></div><p>输出结果：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token class-name"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span>locks<span class="token punctuation">.</span></span>ReentrantLock</span><span class="token annotation punctuation">@64fcd88a</span><span class="token punctuation">[</span><span class="token class-name">Locked</span> by thread <span class="token class-name">Thread</span><span class="token operator">-</span><span class="token class-name">A</span><span class="token punctuation">]</span>
	 holdCount<span class="token operator">:</span> <span class="token number">1</span>
	 queuedLength<span class="token operator">:</span> <span class="token number">2</span>
	 isFair<span class="token operator">:</span> <span class="token boolean">false</span>
	 isLocked<span class="token operator">:</span> <span class="token boolean">true</span>
	 isHeldByCurrentThread<span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token class-name"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span>locks<span class="token punctuation">.</span></span>ReentrantLock</span><span class="token annotation punctuation">@64fcd88a</span><span class="token punctuation">[</span><span class="token class-name">Locked</span> by thread <span class="token class-name">Thread</span><span class="token operator">-</span><span class="token class-name">C</span><span class="token punctuation">]</span>
	 holdCount<span class="token operator">:</span> <span class="token number">1</span>
	 queuedLength<span class="token operator">:</span> <span class="token number">1</span>
	 isFair<span class="token operator">:</span> <span class="token boolean">false</span>
	 isLocked<span class="token operator">:</span> <span class="token boolean">true</span>
	 isHeldByCurrentThread<span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token comment">// ...</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div></details> <h4 id="trylock-方法"><a href="#trylock-方法" class="header-anchor">#</a> tryLock 方法</h4> <p>与无条件获取锁相比，tryLock 有更完善的容错机制</p> <ul><li><code>tryLock()</code> - <strong>可轮询获取锁</strong>。如果成功，则返回 true；如果失败，则返回 false。也就是说，这个方法<strong>无论成败都会立即返回</strong>，获取不到锁（锁已被其他线程获取）时不会一直等待。</li> <li><code>tryLock(long, TimeUnit)</code> - <strong>可定时获取锁</strong>。和 <code>tryLock()</code> 类似，区别仅在于这个方法在<strong>获取不到锁时会等待一定的时间</strong>，在时间期限之内如果还获取不到锁，就返回 false。如果如果一开始拿到锁或者在等待期间内拿到了锁，则返回 true。</li></ul> <details class="custom-block details"><summary>【示例】ReentrantLock 的 tryLock() 操作</summary> <p>修改上个示例中的 <code>execute()</code> 方法</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>lock<span class="token punctuation">.</span><span class="token function">tryLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">3</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
               <span class="token comment">// 略...</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            lock<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">&quot; 获取锁失败&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div></details> <details class="custom-block details"><summary>【示例】ReentrantLock 的 tryLock(long, TimeUnit) 操作</summary> <p>修改上个示例中的 <code>execute()</code> 方法</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>lock<span class="token punctuation">.</span><span class="token function">tryLock</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span>SECONDS<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">try</span> <span class="token punctuation">{</span>
                <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">3</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token comment">// 略...</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
                lock<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">&quot; 获取锁失败&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">&quot; 获取锁超时&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br></div></div></details> <h4 id="lockinterruptibly-方法"><a href="#lockinterruptibly-方法" class="header-anchor">#</a> lockInterruptibly 方法</h4> <ul><li><code>lockInterruptibly()</code> - <strong>可中断获取锁</strong>。可中断获取锁可以在获得锁的同时保持对中断的响应。可中断获取锁比其它获取锁的方式稍微复杂一些，需要两个 <code>try-catch</code> 块（如果在获取锁的操作中抛出了 <code>InterruptedException</code> ，那么可以使用标准的 <code>try-finally</code> 加锁模式）。
<ul><li>举例来说：假设有两个线程同时通过 <code>lock.lockInterruptibly()</code> 获取某个锁时，若线程 A 获取到了锁，则线程 B 只能等待。若此时对线程 B 调用 <code>threadB.interrupt()</code> 方法能够中断线程 B 的等待过程。由于 <code>lockInterruptibly()</code> 的声明中抛出了异常，所以 <code>lock.lockInterruptibly()</code> 必须放在 <code>try</code> 块中或者在调用 <code>lockInterruptibly()</code> 的方法外声明抛出 <code>InterruptedException</code>。</li></ul></li></ul> <blockquote><p>🔔 注意：当一个线程获取了锁之后，是不会被 <code>interrupt()</code> 方法中断的。单独调用 <code>interrupt()</code> 方法不能中断正在运行状态中的线程，只能中断阻塞状态中的线程。因此当通过 <code>lockInterruptibly()</code> 方法获取某个锁时，如果未获取到锁，只有在等待的状态下，才可以响应中断。示例：<code>ReentrantLock</code> 的 <code>lockInterruptibly()</code> 操作</p></blockquote> <details class="custom-block details"><summary>【示例】ReentrantLock 的 lockInterruptibly() 操作</summary> <p>修改上个示例中的 <code>execute()</code> 方法</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
        lock<span class="token punctuation">.</span><span class="token function">lockInterruptibly</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">3</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 略...</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">&quot;被中断&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
        lock<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div></details> <h4 id="newcondition-方法"><a href="#newcondition-方法" class="header-anchor">#</a> newCondition 方法</h4> <p><code>newCondition()</code> - 返回一个绑定到 <code>Lock</code> 对象上的 <code>Condition</code> 实例。</p> <h3 id="reentrantlock-的原理"><a href="#reentrantlock-的原理" class="header-anchor">#</a> ReentrantLock 的原理</h3> <h4 id="reentrantlock-的可见性"><a href="#reentrantlock-的可见性" class="header-anchor">#</a> ReentrantLock 的可见性</h4> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">class</span> <span class="token class-name">X</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Lock</span> rtl <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReentrantLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">int</span> value<span class="token punctuation">;</span>
  <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">addOne</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 获取锁</span>
    rtl<span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
      value<span class="token operator">+=</span><span class="token number">1</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
      <span class="token comment">// 保证锁能释放</span>
      rtl<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><p>ReentrantLock，内部持有一个 volatile 的成员变量 state，获取锁的时候，会读写 state 的值；解锁的时候，也会读写 state 的值（简化后的代码如上所示）。也就是说，在执行 value+=1 之前，程序先读写了一次 volatile 变量 state，在执行 value+=1 之后，又读写了一次 volatile 变量 state。根据相关的 Happens-Before 规则：</p> <ol><li><strong>顺序性规则</strong>：对于线程 T1，value+=1 Happens-Before 释放锁的操作 unlock()；</li> <li><strong>volatile 变量规则</strong>：由于 state = 1 会先读取 state，所以线程 T1 的 unlock() 操作 Happens-Before 线程 T2 的 lock() 操作；</li> <li><strong>传递性规则</strong>：线程 T1 的 value+=1 Happens-Before 线程 T2 的 lock() 操作。</li></ol> <h4 id="reentrantlock-的数据结构"><a href="#reentrantlock-的数据结构" class="header-anchor">#</a> ReentrantLock 的数据结构</h4> <p>阅读 <code>ReentrantLock</code> 的源码，可以发现它有一个核心字段：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Sync</span> sync<span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><ul><li><code>sync</code> - 内部抽象类 <code>ReentrantLock.Sync</code> 对象，<code>Sync</code> 继承自 AQS。它有两个子类：</li> <li><code>ReentrantLock.FairSync</code> - 公平锁。</li> <li><code>ReentrantLock.NonfairSync</code> - 非公平锁。</li></ul> <p>查看源码可以发现，<code>ReentrantLock</code> 实现 <code>Lock</code> 接口其实是调用 <code>ReentrantLock.FairSync</code> 或 <code>ReentrantLock.NonfairSync</code> 中各自的实现，这里不一一列举。</p> <h4 id="reentrantlock-的获取锁和释放锁"><a href="#reentrantlock-的获取锁和释放锁" class="header-anchor">#</a> ReentrantLock 的获取锁和释放锁</h4> <p>ReentrantLock 获取锁和释放锁的接口，从表象看，是调用 <code>ReentrantLock.FairSync</code> 或 <code>ReentrantLock.NonfairSync</code> 中各自的实现；从本质上看，是基于 AQS 的实现。</p> <details class="custom-block details"><summary>See More</summary> <p>仔细阅读源码很容易发现：</p> <ul><li><code>void lock()</code> 调用 Sync 的 lock() 方法。</li> <li><code>void lockInterruptibly()</code> 直接调用 AQS 的 <a href="https://my.oschina.net/u/4569968/blog/4703350#%E8%8E%B7%E5%8F%96%E5%8F%AF%E4%B8%AD%E6%96%AD%E7%9A%84%E7%8B%AC%E5%8D%A0%E9%94%81" target="_blank" rel="noopener noreferrer">获取可中断的独占锁<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 方法 <code>lockInterruptibly()</code>。</li> <li><code>boolean tryLock()</code> 调用 Sync 的 <code>nonfairTryAcquire()</code> 。</li> <li><code>boolean tryLock(long time, TimeUnit unit)</code> 直接调用 AQS 的 <a href="https://my.oschina.net/u/4569968/blog/4703350#%E8%8E%B7%E5%8F%96%E8%B6%85%E6%97%B6%E7%AD%89%E5%BE%85%E5%BC%8F%E7%9A%84%E7%8B%AC%E5%8D%A0%E9%94%81" target="_blank" rel="noopener noreferrer">获取超时等待式的独占锁<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 方法 <code>tryAcquireNanos(int arg, long nanosTimeout)</code>。</li> <li><code>void unlock()</code> 直接调用 AQS 的 <a href="https://my.oschina.net/u/4569968/blog/4703350#%E9%87%8A%E6%94%BE%E7%8B%AC%E5%8D%A0%E9%94%81" target="_blank" rel="noopener noreferrer">释放独占锁<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 方法 <code>release(int arg)</code> 。</li></ul> <p>直接调用 AQS 接口的方法就不再赘述了，其原理在 [AQS 的原理](#AQS 的原理) 中已经用很大篇幅进行过讲解。</p> <p><code>nonfairTryAcquire</code> 方法源码如下：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token comment">// 公平锁和非公平锁都会用这个方法区尝试获取锁</span>
<span class="token keyword">final</span> <span class="token keyword">boolean</span> <span class="token function">nonfairTryAcquire</span><span class="token punctuation">(</span><span class="token keyword">int</span> acquires<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">final</span> <span class="token class-name">Thread</span> current <span class="token operator">=</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">int</span> c <span class="token operator">=</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>c <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">compareAndSetState</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> acquires<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
         <span class="token comment">// 如果同步状态为0，将其设为 acquires，并设置当前线程为排它线程</span>
            <span class="token function">setExclusiveOwnerThread</span><span class="token punctuation">(</span>current<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>current <span class="token operator">==</span> <span class="token function">getExclusiveOwnerThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">int</span> nextc <span class="token operator">=</span> c <span class="token operator">+</span> acquires<span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>nextc <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// overflow</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">&quot;Maximum lock count exceeded&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">setState</span><span class="token punctuation">(</span>nextc<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br></div></div><p>处理流程很简单：</p> <ul><li>如果同步状态为 0，设置同步状态设为 acquires，并设置当前线程为排它线程，然后返回 true，获取锁成功。</li> <li>如果同步状态不为 0 且当前线程为排它线程，设置同步状态为当前状态值+acquires 值，然后返回 true，获取锁成功。</li> <li>否则，返回 false，获取锁失败。</li></ul></details> <h4 id="公平锁和非公平锁"><a href="#公平锁和非公平锁" class="header-anchor">#</a> 公平锁和非公平锁</h4> <p>ReentrantLock 这个类有两个构造函数，一个是无参构造函数，一个是传入 fair 参数的构造函数。fair 参数代表的是锁的公平策略，如果传入 true 就表示需要构造一个公平锁，反之则表示要构造一个非公平锁。</p> <p>锁都对应着一个等待队列，如果一个线程没有获得锁，就会进入等待队列，当有线程释放锁的时候，就需要从等待队列中唤醒一个等待的线程。如果是公平锁，唤醒的策略就是谁等待的时间长，就唤醒谁，很公平；如果是非公平锁，则不提供这个公平保证，有可能等待时间短的线程反而先被唤醒。</p> <p><code>lock</code> 方法在公平锁和非公平锁中的实现：</p> <p>二者的区别仅在于申请非公平锁时，如果同步状态为 0，尝试将其设为 1，如果成功，直接将当前线程置为排它线程；否则和公平锁一样，调用 AQS 获取独占锁方法 <code>acquire</code>。</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token comment">// 非公平锁实现</span>
<span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">compareAndSetState</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token comment">// 如果同步状态为0，将其设为1，并设置当前线程为排它线程</span>
        <span class="token function">setExclusiveOwnerThread</span><span class="token punctuation">(</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">else</span>
    <span class="token comment">// 调用 AQS 获取独占锁方法 acquire</span>
        <span class="token function">acquire</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 公平锁实现</span>
<span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 调用 AQS 获取独占锁方法 acquire</span>
    <span class="token function">acquire</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><h2 id="reentrantreadwritelock"><a href="#reentrantreadwritelock" class="header-anchor">#</a> ReentrantReadWriteLock</h2> <p><code>ReadWriteLock</code> 适用于<strong>读多写少的场景</strong>。</p> <p><code>ReentrantReadWriteLock</code> 类是 <code>ReadWriteLock</code> 接口的具体实现，它是一个可重入的读写锁。<code>ReentrantReadWriteLock</code> 维护了一对读写锁，将读写锁分开，有利于提高并发效率。</p> <p>读写锁，并不是 Java 语言特有的，而是一个广为使用的通用技术，所有的读写锁都遵守以下三条基本原则：</p> <ul><li>允许多个线程同时读共享变量；</li> <li>只允许一个线程写共享变量；</li> <li>如果一个写线程正在执行写操作，此时禁止读线程读共享变量。</li></ul> <p>读写锁与互斥锁的一个重要区别就是<strong>读写锁允许多个线程同时读共享变量</strong>，而互斥锁是不允许的，这是读写锁在读多写少场景下性能优于互斥锁的关键。但<strong>读写锁的写操作是互斥的</strong>，当一个线程在写共享变量的时候，是不允许其他线程执行写操作和读操作。</p> <h3 id="reentrantreadwritelock-的特性"><a href="#reentrantreadwritelock-的特性" class="header-anchor">#</a> ReentrantReadWriteLock 的特性</h3> <p>ReentrantReadWriteLock 的特性如下：</p> <ul><li><strong>ReentrantReadWriteLock 适用于读多写少的场景</strong>。如果是写多读少的场景，由于 <code>ReentrantReadWriteLock</code> 其内部实现比 <code>ReentrantLock</code> 复杂，性能可能反而要差一些。如果存在这样的问题，需要具体问题具体分析。由于 <code>ReentrantReadWriteLock</code> 的读写锁（<code>ReadLock</code>、<code>WriteLock</code>）都实现了 <code>Lock</code> 接口，所以要替换为 <code>ReentrantLock</code> 也较为容易。</li> <li><code>ReentrantReadWriteLock</code> 实现了 <code>ReadWriteLock</code> 接口，支持了 <code>ReentrantLock</code> 所不具备的读写锁分离。<code>ReentrantReadWriteLock</code> 维护了一对读写锁（<code>ReadLock</code>、<code>WriteLock</code>）。将读写锁分开，有利于提高并发效率。<code>ReentrantReadWriteLock</code> 的加锁策略是：<strong>允许多个读操作并发执行，但每次只允许一个写操作</strong>。</li> <li><code>ReentrantReadWriteLock</code> 为读写锁都提供了可重入的加锁语义。</li> <li><code>ReentrantReadWriteLock</code> 支持公平锁和非公平锁（默认）两种模式。</li></ul> <p><code>ReadWriteLock</code> 接口定义如下：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">ReadWriteLock</span> <span class="token punctuation">{</span>
    <span class="token class-name">Lock</span> <span class="token function">readLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">Lock</span> <span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><ul><li><code>readLock</code> - 返回用于读操作的锁（<code>ReadLock</code>）。</li> <li><code>writeLock</code> - 返回用于写操作的锁（<code>WriteLock</code>）。</li></ul> <p>在读写锁和写入锁之间的交互可以采用多种实现方式，<code>ReadWriteLock</code> 的一些可选实现包括：</p> <ul><li><strong>释放优先</strong> - 当一个写入操作释放写锁，并且队列中同时存在读线程和写线程，那么应该优先选择读线程、写线程，还是最先发出请求的线程？</li> <li><strong>读线程插队</strong> - 如果锁是由读线程持有，但有写线程正在等待，那么新到达的读线程能否立即获得访问权，还是应该在写线程后面等待？如果允许读线程插队到写线程之前，那么将提高并发性，但可能造成线程饥饿问题。</li> <li><strong>重入性</strong> - 读锁和写锁是否是可重入的？</li> <li><strong>降级</strong> - 如果一个线程持有写入锁，那么它能否在不释放该锁的情况下获得读锁？这可能会使得写锁被降级为读锁，同时不允许其他写线程修改被保护的资源。</li> <li><strong>升级</strong> - 读锁能否优先于其他正在等待的读线程和写线程而升级为一个写锁？在大多数的读写锁实现中并不支持升级，因为如果没有显式的升级操作，那么很容易造成死锁。</li></ul> <h3 id="reentrantreadwritelock-的用法"><a href="#reentrantreadwritelock-的用法" class="header-anchor">#</a> ReentrantReadWriteLock 的用法</h3> <h4 id="reentrantreadwritelock-的构造方法"><a href="#reentrantreadwritelock-的构造方法" class="header-anchor">#</a> ReentrantReadWriteLock 的构造方法</h4> <p><code>ReentrantReadWriteLock</code> 和 <code>ReentrantLock</code> 一样，也有两个构造方法，且用法相似。</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token class-name">ReentrantReadWriteLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token class-name">ReentrantReadWriteLock</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> fair<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><ul><li><code>ReentrantReadWriteLock()</code> - 默认构造方法会初始化一个<strong>非公平锁（NonfairSync）</strong>。在非公平的锁中，线程获得锁的顺序是不确定的。写线程降级为读线程是可以的，但读线程升级为写线程是不可以的（这样会导致死锁）。</li> <li><code>ReentrantReadWriteLock(boolean)</code> - <code>new ReentrantLock(true)</code> 会初始化一个<strong>公平锁（FairSync）</strong>。对于公平锁，等待时间最长的线程将优先获得锁。如果这个锁是读线程持有，则另一个线程请求写锁，那么其他读线程都不能获得读锁，直到写线程释放写锁。</li></ul> <h4 id="reentrantreadwritelock-的使用实例"><a href="#reentrantreadwritelock-的使用实例" class="header-anchor">#</a> ReentrantReadWriteLock 的使用实例</h4> <p>在 <a href="#reentrantreadwritelock-%E7%9A%84%E7%89%B9%E6%80%A7"><code>ReentrantReadWriteLock</code> 的特性</a> 中已经介绍过，<code>ReentrantReadWriteLock</code> 的读写锁（<code>ReadLock</code>、<code>WriteLock</code>）都实现了 <code>Lock</code> 接口，所以其各自独立的使用方式与 <code>ReentrantLock</code> 一样，这里不再赘述。</p> <p><code>ReentrantReadWriteLock</code> 与 <code>ReentrantLock</code> 用法上的差异，主要在于读写锁的配合使用。本文以一个典型使用场景来进行讲解。</p> <details class="custom-block details"><summary>【示例】基于 ReadWriteLock 实现一个简单的泛型无界缓存</summary> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token comment">/**
 * 简单的无界缓存实现
 * &lt;p&gt;
 * 使用 WeakHashMap 存储键值对。WeakHashMap 中存储的对象是弱引用，JVM GC 时会自动清除没有被引用的弱引用对象。
 */</span>
<span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">UnboundedCache</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">K</span><span class="token punctuation">,</span> <span class="token class-name">V</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">K</span><span class="token punctuation">,</span> <span class="token class-name">V</span><span class="token punctuation">&gt;</span></span> cacheMap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WeakHashMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">ReadWriteLock</span> cacheLock <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReentrantReadWriteLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token class-name">V</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token class-name">K</span> key<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        cacheLock<span class="token punctuation">.</span><span class="token function">readLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">V</span> value<span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            value <span class="token operator">=</span> cacheMap<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">String</span> log <span class="token operator">=</span> <span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string">&quot;%s 读数据 %s:%s&quot;</span><span class="token punctuation">,</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> key<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>log<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            cacheLock<span class="token punctuation">.</span><span class="token function">readLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> value<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token class-name">V</span> <span class="token function">put</span><span class="token punctuation">(</span><span class="token class-name">K</span> key<span class="token punctuation">,</span> <span class="token class-name">V</span> value<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        cacheLock<span class="token punctuation">.</span><span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            cacheMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">String</span> log <span class="token operator">=</span> <span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string">&quot;%s 写入数据 %s:%s&quot;</span><span class="token punctuation">,</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> key<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>log<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            cacheLock<span class="token punctuation">.</span><span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> value<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token class-name">V</span> <span class="token function">remove</span><span class="token punctuation">(</span><span class="token class-name">K</span> key<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        cacheLock<span class="token punctuation">.</span><span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span> cacheMap<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            cacheLock<span class="token punctuation">.</span><span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        cacheLock<span class="token punctuation">.</span><span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>cacheMap<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
            cacheLock<span class="token punctuation">.</span><span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br></div></div><p>说明：</p> <ul><li>使用 <code>WeakHashMap</code> 而不是 <code>HashMap</code> 来存储键值对。<code>WeakHashMap</code> 中存储的对象是弱引用，JVM GC 时会自动清除没有被引用的弱引用对象。</li> <li>向 <code>Map</code> 写数据前加写锁，写完后，释放写锁。</li> <li>向 <code>Map</code> 读数据前加读锁，读完后，释放读锁。</li></ul> <p>测试其线程安全性：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token comment">/**
 * @author &lt;a href=&quot;mailto:forbreak@163.com&quot;&gt;Zhang Peng&lt;/a&gt;
 * @since 2020-01-01
 */</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ReentrantReadWriteLockDemo</span> <span class="token punctuation">{</span>

    <span class="token keyword">static</span> <span class="token class-name">UnboundedCache</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Integer</span><span class="token punctuation">,</span> <span class="token class-name">Integer</span><span class="token punctuation">&gt;</span></span> cache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UnboundedCache</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">ExecutorService</span> executorService <span class="token operator">=</span> <span class="token class-name">Executors</span><span class="token punctuation">.</span><span class="token function">newCachedThreadPool</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">20</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            executorService<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            cache<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        executorService<span class="token punctuation">.</span><span class="token function">shutdown</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/** 线程任务每次向缓存中写入 3 个随机值，key 固定 */</span>
    <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">MyThread</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span> <span class="token punctuation">{</span>

        <span class="token annotation punctuation">@Override</span>
        <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token class-name">Random</span> random <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">3</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                cache<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> random<span class="token punctuation">.</span><span class="token function">nextInt</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br></div></div><p>说明：示例中，通过线程池启动 20 个并发任务。任务每次向缓存中写入 3 个随机值，key 固定；然后主线程每次固定读取缓存中第一个 key 的值。</p> <p>输出结果：</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>main 读数据 0:null
pool-1-thread-1 写入数据 0:16
pool-1-thread-1 写入数据 1:58
pool-1-thread-1 写入数据 2:50
main 读数据 0:16
pool-1-thread-1 写入数据 0:85
pool-1-thread-1 写入数据 1:76
pool-1-thread-1 写入数据 2:46
pool-1-thread-2 写入数据 0:21
pool-1-thread-2 写入数据 1:41
pool-1-thread-2 写入数据 2:63
main 读数据 0:21
main 读数据 0:21
// ...
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div></details> <h3 id="reentrantreadwritelock-的原理"><a href="#reentrantreadwritelock-的原理" class="header-anchor">#</a> ReentrantReadWriteLock 的原理</h3> <h4 id="reentrantreadwritelock-的数据结构"><a href="#reentrantreadwritelock-的数据结构" class="header-anchor">#</a> ReentrantReadWriteLock 的数据结构</h4> <p>阅读 ReentrantReadWriteLock 的源码，可以发现它有三个核心字段：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token comment">/** Inner class providing readlock */</span>
<span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">ReentrantReadWriteLock<span class="token punctuation">.</span>ReadLock</span> readerLock<span class="token punctuation">;</span>
<span class="token comment">/** Inner class providing writelock */</span>
<span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">ReentrantReadWriteLock<span class="token punctuation">.</span>WriteLock</span> writerLock<span class="token punctuation">;</span>
<span class="token comment">/** Performs all synchronization mechanics */</span>
<span class="token keyword">final</span> <span class="token class-name">Sync</span> sync<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token class-name">ReentrantReadWriteLock<span class="token punctuation">.</span>WriteLock</span> <span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> writerLock<span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token class-name">ReentrantReadWriteLock<span class="token punctuation">.</span>ReadLock</span>  <span class="token function">readLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token punctuation">{</span> <span class="token keyword">return</span> readerLock<span class="token punctuation">;</span> <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><ul><li><code>sync</code> - 内部类 <code>ReentrantReadWriteLock.Sync</code> 对象。与 <code>ReentrantLock</code> 类似，它有两个子类：<code>ReentrantReadWriteLock.FairSync</code> 和 <code>ReentrantReadWriteLock.NonfairSync</code> ，分别表示公平锁和非公平锁的实现。</li> <li><code>readerLock</code> - 内部类 <code>ReentrantReadWriteLock.ReadLock</code> 对象，这是一把读锁。</li> <li><code>writerLock</code> - 内部类 <code>ReentrantReadWriteLock.WriteLock</code> 对象，这是一把写锁。</li></ul> <h4 id="reentrantreadwritelock-的获取锁和释放锁"><a href="#reentrantreadwritelock-的获取锁和释放锁" class="header-anchor">#</a> ReentrantReadWriteLock 的获取锁和释放锁</h4> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">ReadLock</span> <span class="token keyword">implements</span> <span class="token class-name">Lock</span><span class="token punctuation">,</span> <span class="token class-name"><span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span>Serializable</span> <span class="token punctuation">{</span>

    <span class="token comment">// 调用 AQS 获取共享锁方法</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        sync<span class="token punctuation">.</span><span class="token function">acquireShared</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 调用 AQS 释放共享锁方法</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        sync<span class="token punctuation">.</span><span class="token function">releaseShared</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">WriteLock</span> <span class="token keyword">implements</span> <span class="token class-name">Lock</span><span class="token punctuation">,</span> <span class="token class-name"><span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span>Serializable</span> <span class="token punctuation">{</span>

    <span class="token comment">// 调用 AQS 获取独占锁方法</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        sync<span class="token punctuation">.</span><span class="token function">acquire</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 调用 AQS 释放独占锁方法</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        sync<span class="token punctuation">.</span><span class="token function">release</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br></div></div><h2 id="stampedlock"><a href="#stampedlock" class="header-anchor">#</a> StampedLock</h2> <p>ReadWriteLock 支持两种模式：一种是读锁，一种是写锁。而 StampedLock 支持三种模式，分别是：<strong>写锁</strong>、<strong>悲观读锁</strong>和<strong>乐观读</strong>。其中，写锁、悲观读锁的语义和 ReadWriteLock 的写锁、读锁的语义非常类似，允许多个线程同时获取悲观读锁，但是只允许一个线程获取写锁，写锁和悲观读锁是互斥的。不同的是：StampedLock 里的写锁和悲观读锁加锁成功之后，都会返回一个 stamp；然后解锁的时候，需要传入这个 stamp。</p> <blockquote><p>注意这里，用的是“乐观读”这个词，而不是“乐观读锁”，是要提醒你，<strong>乐观读这个操作是无锁的</strong>，所以相比较 ReadWriteLock 的读锁，乐观读的性能更好一些。</p></blockquote> <p>StampedLock 的性能之所以比 ReadWriteLock 还要好，其关键是 <strong>StampedLock 支持乐观读</strong>的方式。</p> <ul><li>ReadWriteLock 支持多个线程同时读，但是当多个线程同时读的时候，所有的写操作会被阻塞；</li> <li>而 StampedLock 提供的乐观读，是允许一个线程获取写锁的，也就是说不是所有的写操作都被阻塞。</li></ul> <p>对于读多写少的场景 StampedLock 性能很好，简单的应用场景基本上可以替代 ReadWriteLock，但是<strong>StampedLock 的功能仅仅是 ReadWriteLock 的子集</strong>，在使用的时候，还是有几个地方需要注意一下。</p> <ul><li><strong>StampedLock 不支持重入</strong></li> <li>StampedLock 的悲观读锁、写锁都不支持条件变量。</li> <li>如果线程阻塞在 StampedLock 的 readLock() 或者 writeLock() 上时，此时调用该阻塞线程的 interrupt() 方法，会导致 CPU 飙升。<strong>使用 StampedLock 一定不要调用中断操作，如果需要支持中断功能，一定使用可中断的悲观读锁 readLockInterruptibly() 和写锁 writeLockInterruptibly()</strong>。</li></ul> <details class="custom-block details"><summary>【示例】StampedLock 阻塞时，调用 interrupt() 导致 CPU 飙升</summary> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">final</span> <span class="token class-name">StampedLock</span> lock
  <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StampedLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Thread</span> T1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token punctuation">{</span>
  <span class="token comment">// 获取写锁</span>
  lock<span class="token punctuation">.</span><span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 永远阻塞在此处，不释放写锁</span>
  <span class="token class-name">LockSupport</span><span class="token punctuation">.</span><span class="token function">park</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
T1<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 保证 T1 获取写锁</span>
<span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Thread</span> T2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span>
  <span class="token comment">// 阻塞在悲观读锁</span>
  lock<span class="token punctuation">.</span><span class="token function">readLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
T2<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 保证 T2 阻塞在读锁</span>
<span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 中断线程 T2</span>
<span class="token comment">// 会导致线程 T2 所在 CPU 飙升</span>
T2<span class="token punctuation">.</span><span class="token function">interrupt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
T2<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br></div></div><p>【示例】StampedLock 读模板：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">final</span> <span class="token class-name">StampedLock</span> sl <span class="token operator">=</span>
  <span class="token keyword">new</span> <span class="token class-name">StampedLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 乐观读</span>
<span class="token keyword">long</span> stamp <span class="token operator">=</span>
  sl<span class="token punctuation">.</span><span class="token function">tryOptimisticRead</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 读入方法局部变量</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token comment">// 校验 stamp</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>sl<span class="token punctuation">.</span><span class="token function">validate</span><span class="token punctuation">(</span>stamp<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token comment">// 升级为悲观读锁</span>
  stamp <span class="token operator">=</span> sl<span class="token punctuation">.</span><span class="token function">readLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">try</span> <span class="token punctuation">{</span>
    <span class="token comment">// 读入方法局部变量</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
  <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
    <span class="token comment">// 释放悲观读锁</span>
    sl<span class="token punctuation">.</span><span class="token function">unlockRead</span><span class="token punctuation">(</span>stamp<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 使用方法局部变量执行业务操作</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br></div></div><p>【示例】StampedLock 写模板：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">long</span> stamp <span class="token operator">=</span> sl<span class="token punctuation">.</span><span class="token function">writeLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
  <span class="token comment">// 写共享变量</span>
  <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
  sl<span class="token punctuation">.</span><span class="token function">unlockWrite</span><span class="token punctuation">(</span>stamp<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div></details> <h2 id="aqs"><a href="#aqs" class="header-anchor">#</a> AQS</h2> <blockquote><p><code>AbstractQueuedSynchronizer</code>（简称 <strong>AQS</strong>）是<strong>队列同步器</strong>，顾名思义，其主要作用是处理同步。它是并发锁和很多同步工具类的实现基石（如 <code>ReentrantLock</code>、<code>ReentrantReadWriteLock</code>、<code>CountDownLatch</code>、<code>Semaphore</code>、<code>FutureTask</code> 等）。</p></blockquote> <h3 id="aqs-的要点"><a href="#aqs-的要点" class="header-anchor">#</a> AQS 的要点</h3> <p><strong>AQS 提供了对独享锁与共享锁的支持</strong>。</p> <p>在 <code>java.util.concurrent.locks</code> 包中的相关锁（常用的有 <code>ReentrantLock</code>、 <code>ReadWriteLock</code>）都是基于 AQS 来实现。这些锁都没有直接继承 AQS，而是<strong>定义了一个 <code>Sync</code> 类去继承 AQS</strong>。为什么要这样呢？因为锁面向的是使用用户，而同步器面向的则是线程控制，那么在锁的实现中聚合同步器而不是直接继承 AQS 就可以很好的隔离二者所关注的事情。</p> <h3 id="aqs-的应用"><a href="#aqs-的应用" class="header-anchor">#</a> AQS 的应用</h3> <p><strong>AQS 提供了对独享锁与共享锁的支持</strong>。</p> <h4 id="独享锁-api"><a href="#独享锁-api" class="header-anchor">#</a> 独享锁 API</h4> <p>获取、释放独享锁的主要 API 如下：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">acquire</span><span class="token punctuation">(</span><span class="token keyword">int</span> arg<span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">acquireInterruptibly</span><span class="token punctuation">(</span><span class="token keyword">int</span> arg<span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> <span class="token function">tryAcquireNanos</span><span class="token punctuation">(</span><span class="token keyword">int</span> arg<span class="token punctuation">,</span> <span class="token keyword">long</span> nanosTimeout<span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> <span class="token function">release</span><span class="token punctuation">(</span><span class="token keyword">int</span> arg<span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><ul><li><p><code>acquire</code> - 获取独占锁。</p></li> <li><p><code>acquireInterruptibly</code> - 获取可中断的独占锁。</p></li> <li><div class="language- line-numbers-mode"><pre class="language-text"><code>tryAcquireNanos
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>- 尝试在指定时间内获取可中断的独占锁。在以下三种情况下回返回：</p> <ul><li>在超时时间内，当前线程成功获取了锁；</li> <li>当前线程在超时时间内被中断；</li> <li>超时时间结束，仍未获得锁返回 false。</li></ul></li> <li><p><code>release</code> - 释放独占锁。</p></li></ul> <h4 id="共享锁-api"><a href="#共享锁-api" class="header-anchor">#</a> 共享锁 API</h4> <p>获取、释放共享锁的主要 API 如下：</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">acquireShared</span><span class="token punctuation">(</span><span class="token keyword">int</span> arg<span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">acquireSharedInterruptibly</span><span class="token punctuation">(</span><span class="token keyword">int</span> arg<span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> <span class="token function">tryAcquireSharedNanos</span><span class="token punctuation">(</span><span class="token keyword">int</span> arg<span class="token punctuation">,</span> <span class="token keyword">long</span> nanosTimeout<span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> <span class="token function">releaseShared</span><span class="token punctuation">(</span><span class="token keyword">int</span> arg<span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><ul><li><code>acquireShared</code> - 获取共享锁。</li> <li><code>acquireSharedInterruptibly</code> - 获取可中断的共享锁。</li> <li><code>tryAcquireSharedNanos</code> - 尝试在指定时间内获取可中断的共享锁。</li> <li><code>release</code> - 释放共享锁。</li></ul> <h3 id="aqs-的原理"><a href="#aqs-的原理" class="header-anchor">#</a> AQS 的原理</h3> <blockquote><p>ASQ 原理要点：</p> <ul><li>AQS 使用一个整型的 <code>volatile</code> 变量来 <strong>维护同步状态</strong>。状态的意义由子类赋予。</li> <li>AQS 维护了一个 FIFO 的双链表，用来存储获取锁失败的线程。</li></ul> <p>AQS 围绕同步状态提供两种基本操作“获取”和“释放”，并提供一系列判断和处理方法，简单说几点：</p> <ul><li>state 是独占的，还是共享的；</li> <li>state 被获取后，其他线程需要等待；</li> <li>state 被释放后，唤醒等待线程；</li> <li>线程等不及时，如何退出等待。</li></ul> <p>至于线程是否可以获得 state，如何释放 state，就不是 AQS 关心的了，要由子类具体实现。</p></blockquote> <h4 id="aqs-的数据结构"><a href="#aqs-的数据结构" class="header-anchor">#</a> AQS 的数据结构</h4> <p>阅读 AQS 的源码，可以发现：AQS 继承自 <code>AbstractOwnableSynchronize</code>。</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">AbstractQueuedSynchronizer</span>
    <span class="token keyword">extends</span> <span class="token class-name">AbstractOwnableSynchronizer</span>
    <span class="token keyword">implements</span> <span class="token class-name"><span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span>Serializable</span> <span class="token punctuation">{</span>

    <span class="token comment">/** 等待队列的队头，懒加载。只能通过 setHead 方法修改。 */</span>
    <span class="token keyword">private</span> <span class="token keyword">transient</span> <span class="token keyword">volatile</span> <span class="token class-name">Node</span> head<span class="token punctuation">;</span>
    <span class="token comment">/** 等待队列的队尾，懒加载。只能通过 enq 方法添加新的等待节点。*/</span>
    <span class="token keyword">private</span> <span class="token keyword">transient</span> <span class="token keyword">volatile</span> <span class="token class-name">Node</span> tail<span class="token punctuation">;</span>
    <span class="token comment">/** 同步状态 */</span>
    <span class="token keyword">private</span> <span class="token keyword">volatile</span> <span class="token keyword">int</span> state<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><ul><li><code>state</code> - AQS 使用一个整型的 <code>volatile</code> 变量来 <strong>维护同步状态</strong>。
<ul><li>这个整数状态的意义由子类来赋予，如<code>ReentrantLock</code> 中该状态值表示所有者线程已经重复获取该锁的次数，<code>Semaphore</code> 中该状态值表示剩余的许可数量。</li></ul></li> <li><code>head</code> 和 <code>tail</code> - AQS <strong>维护了一个 Node 类型（AQS 的内部类）的双链表来完成同步状态的管理</strong>。这个双链表是一个双向的 FIFO 队列，通过 <code>head</code> 和 <code>tail</code> 指针进行访问。当 <strong>有线程获取锁失败后，就被添加到队列末尾</strong>。</li></ul> <p><img src="https://fastly.jsdelivr.net/gh/huhuhan/img@master/md/pic/up-9be85c9be33d64d5efa4aa8bee255c4711c.png" alt=""></p> <p>再来看一下 <code>Node</code> 的源码</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">Node</span> <span class="token punctuation">{</span>
    <span class="token comment">/** 该等待同步的节点处于共享模式 */</span>
    <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">Node</span> SHARED <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Node</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">/** 该等待同步的节点处于独占模式 */</span>
    <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">Node</span> EXCLUSIVE <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

    <span class="token comment">/** 线程等待状态，状态值有: 0、1、-1、-2、-3 */</span>
    <span class="token keyword">volatile</span> <span class="token keyword">int</span> waitStatus<span class="token punctuation">;</span>
    <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> CANCELLED <span class="token operator">=</span>  <span class="token number">1</span><span class="token punctuation">;</span>
    <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> SIGNAL    <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>
    <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> CONDITION <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">;</span>
    <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> PROPAGATE <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">3</span><span class="token punctuation">;</span>

    <span class="token comment">/** 前驱节点 */</span>
    <span class="token keyword">volatile</span> <span class="token class-name">Node</span> prev<span class="token punctuation">;</span>
    <span class="token comment">/** 后继节点 */</span>
    <span class="token keyword">volatile</span> <span class="token class-name">Node</span> next<span class="token punctuation">;</span>
    <span class="token comment">/** 等待锁的线程 */</span>
    <span class="token keyword">volatile</span> <span class="token class-name">Thread</span> thread<span class="token punctuation">;</span>

  	<span class="token comment">/** 和节点是否共享有关 */</span>
    <span class="token class-name">Node</span> nextWaiter<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br></div></div><p>很显然，Node 是一个双链表结构。</p> <ul><li><code>waitStatus</code> - <code>Node</code> 使用一个整型的 <code>volatile</code> 变量来 维护 AQS 同步队列中线程节点的状态。<code>waitStatus</code> 有五个状态值：
<ul><li><code>CANCELLED(1)</code> - 此状态表示：该节点的线程可能由于超时或被中断而 <strong>处于被取消(作废)状态</strong>，一旦处于这个状态，表示这个节点应该从等待队列中移除。</li> <li><code>SIGNAL(-1)</code> - 此状态表示：<strong>后继节点会被挂起</strong>，因此在当前节点释放锁或被取消之后，必须唤醒(<code>unparking</code>)其后继结点。</li> <li><code>CONDITION(-2)</code> - 此状态表示：该节点的线程 <strong>处于等待条件状态</strong>，不会被当作是同步队列上的节点，直到被唤醒(<code>signal</code>)，设置其值为 0，再重新进入阻塞状态。</li> <li><code>PROPAGATE(-3)</code> - 此状态表示：下一个 <code>acquireShared</code> 应无条件传播。</li> <li>0 - 非以上状态。</li></ul></li></ul> <h4 id="独占锁的获取和释放"><a href="#独占锁的获取和释放" class="header-anchor">#</a> 独占锁的获取和释放</h4> <h5 id="获取独占锁"><a href="#获取独占锁" class="header-anchor">#</a> 获取独占锁</h5> <p>AQS 中使用 <code>acquire(int arg)</code> 方法获取独占锁，其大致流程如下：</p> <ol><li>先尝试获取同步状态，如果获取同步状态成功，则结束方法，直接返回。</li> <li>如果获取同步状态不成功，AQS 会不断尝试利用 CAS 操作将当前线程插入等待同步队列的队尾，直到成功为止。</li> <li>接着，不断尝试为等待队列中的线程节点获取独占锁。</li></ol> <p><img src="https://fastly.jsdelivr.net/gh/huhuhan/img@master/md/pic/up-f61f5de99d6bafb83e0546dccee8aec2bde.png" alt=""></p> <p><img src="https://fastly.jsdelivr.net/gh/huhuhan/img@master/md/pic/up-43054235fc883432b2a2d2363216f9e572d.png" alt=""></p> <p>详细流程可以用下图来表示，请结合源码来理解（一图胜千言）：</p> <p><img src="https://fastly.jsdelivr.net/gh/huhuhan/img@master/md/pic/up-c0bd1a1dca1d678b34fa15012d8df945958.png" alt=""></p> <h5 id="释放独占锁"><a href="#释放独占锁" class="header-anchor">#</a> 释放独占锁</h5> <p>AQS 中使用 <code>release(int arg)</code> 方法释放独占锁，其大致流程如下：</p> <ol><li>先尝试获取解锁线程的同步状态，如果获取同步状态不成功，则结束方法，直接返回。</li> <li>如果获取同步状态成功，AQS 会尝试唤醒当前线程节点的后继节点。</li></ol> <h5 id="获取可中断的独占锁"><a href="#获取可中断的独占锁" class="header-anchor">#</a> 获取可中断的独占锁</h5> <p>AQS 中使用 <code>acquireInterruptibly(int arg)</code> 方法获取可中断的独占锁。</p> <p><code>acquireInterruptibly(int arg)</code> 实现方式<strong>相较于获取独占锁方法（ acquire）非常相似</strong>，区别仅在于它会<strong>通过 Thread.interrupted 检测当前线程是否被中断</strong>，如果是，则立即抛出中断异常（<code>InterruptedException</code>）。</p> <h5 id="获取超时等待式的独占锁"><a href="#获取超时等待式的独占锁" class="header-anchor">#</a> 获取超时等待式的独占锁</h5> <p>AQS 中使用 <code>tryAcquireNanos(int arg)</code> 方法获取超时等待的独占锁。</p> <p>doAcquireNanos 的实现方式 <strong>相较于获取独占锁方法（ acquire）非常相似</strong>，区别在于它会根据超时时间和当前时间计算出截止时间。在获取锁的流程中，会不断判断是否超时，如果超时，直接返回 false；如果没超时，则用 <code>LockSupport.parkNanos</code> 来阻塞当前线程。</p> <h4 id="共享锁的获取和释放"><a href="#共享锁的获取和释放" class="header-anchor">#</a> 共享锁的获取和释放</h4> <h5 id="获取共享锁"><a href="#获取共享锁" class="header-anchor">#</a> 获取共享锁</h5> <p>AQS 中使用 <code>acquireShared(int arg)</code> 方法获取共享锁。</p> <p><code>acquireShared</code> 方法和 <code>acquire</code> 方法的逻辑很相似，区别仅在于自旋的条件以及节点出队的操作有所不同。</p> <p>成功获得共享锁的条件如下：</p> <ul><li><code>tryAcquireShared(arg)</code> 返回值大于等于 0 （这意味着共享锁的 permit 还没有用完）。</li> <li>当前节点的前驱节点是头结点。</li></ul> <h5 id="释放共享锁"><a href="#释放共享锁" class="header-anchor">#</a> 释放共享锁</h5> <p>AQS 中使用 <code>releaseShared(int arg)</code> 方法释放共享锁。</p> <p><code>releaseShared</code> 首先会尝试释放同步状态，如果成功，则解锁一个或多个后继线程节点。释放共享锁和释放独享锁流程大体相似，区别在于：</p> <p>对于独享模式，如果需要 SIGNAL，释放仅相当于调用头节点的 <code>unparkSuccessor</code>。</p> <h5 id="获取可中断的共享锁"><a href="#获取可中断的共享锁" class="header-anchor">#</a> 获取可中断的共享锁</h5> <p>AQS 中使用 <code>acquireSharedInterruptibly(int arg)</code> 方法获取可中断的共享锁。</p> <p><code>acquireSharedInterruptibly</code> 方法与 <code>acquireInterruptibly</code> 几乎一致，不再赘述。</p> <h5 id="获取超时等待式的共享锁"><a href="#获取超时等待式的共享锁" class="header-anchor">#</a> 获取超时等待式的共享锁</h5> <p>AQS 中使用 <code>tryAcquireSharedNanos(int arg)</code> 方法获取超时等待式的共享锁。</p> <p><code>tryAcquireSharedNanos</code> 方法与 <code>tryAcquireNanos</code> 几乎一致，不再赘述。</p> <h2 id="死锁"><a href="#死锁" class="header-anchor">#</a> 死锁</h2> <h3 id="什么是死锁"><a href="#什么是死锁" class="header-anchor">#</a> 什么是死锁</h3> <p>死锁是一种特定的程序状态，在实体之间，由于循环依赖导致彼此一直处于等待之中，没有任何个体可以继续前进。死锁不仅仅是在线程之间会发生，存在资源独占的进程之间同样也 可能出现死锁。通常来说，我们大多是聚焦在多线程场景中的死锁，指两个或多个线程之间，由于互相持有对方需要的锁，而永久处于阻塞的状态。</p> <h3 id="如何定位死锁"><a href="#如何定位死锁" class="header-anchor">#</a> 如何定位死锁</h3> <p>定位死锁最常见的方式就是利用 jstack 等工具获取线程栈，然后定位互相之间的依赖关系，进而找到死锁。如果是比较明显的死锁，往往 jstack 等就能直接定位，类似 JConsole 甚至可以在图形界面进行有限的死锁检测。</p> <p>如果我们是开发自己的管理工具，需要用更加程序化的方式扫描服务进程、定位死锁，可以考虑使用 Java 提供的标准管理 API，<code>ThreadMXBean</code>，其直接就提供了 <code>findDeadlockedThreads()</code> 方法用于定位。</p> <h3 id="如何避免死锁"><a href="#如何避免死锁" class="header-anchor">#</a> 如何避免死锁</h3> <p>基本上死锁的发生是因为：</p> <ul><li>互斥，类似 Java 中 Monitor 都是独占的。</li> <li>长期保持互斥，在使用结束之前，不会释放，也不能被其他线程抢占。</li> <li>循环依赖，多个个体之间出现了锁的循环依赖，彼此依赖上一环释放锁。</li></ul> <p>由此，我们可以分析出避免死锁的思路和方法。</p> <ul><li>避免一个线程同时获取多个锁。</li></ul> <p>避免一个线程在锁内同时占用多个资源，尽量保证每个锁只占用一个资源。</p> <p>尝试使用定时锁 <code>lock.tryLock(timeout)</code>，避免锁一直不能释放。</p> <p>对于数据库锁，加锁和解锁必须在一个数据库连接中里，否则会出现解锁失败的情况。</p></div> <footer class="page-edit" style="display:none;"><!----> <div class="last-updated"><span class="prefix">Last Updated: </span> <span class="time">2022/5/27 上午11:15:53</span></div></footer> <!----> <!----></main> <!----></div></div></div></div><div class="global-ui"><div class="back-to-ceiling" style="right:1rem;bottom:6rem;width:2.5rem;height:2.5rem;border-radius:.25rem;line-height:2.5rem;display:none;" data-v-c6073ba8 data-v-c6073ba8><svg t="1574745035067" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5404" class="icon" data-v-c6073ba8><path d="M526.60727968 10.90185116a27.675 27.675 0 0 0-29.21455937 0c-131.36607665 82.28402758-218.69155461 228.01873535-218.69155402 394.07834331a462.20625001 462.20625001 0 0 0 5.36959153 69.94390903c1.00431239 6.55289093-0.34802892 13.13561351-3.76865779 18.80351572-32.63518765 54.11355614-51.75690182 118.55860487-51.7569018 187.94566865a371.06718723 371.06718723 0 0 0 11.50484808 91.98906777c6.53300375 25.50556257 41.68394495 28.14064038 52.69160883 4.22606766 17.37162448-37.73630017 42.14135425-72.50938081 72.80769204-103.21549295 2.18761121 3.04276886 4.15646224 6.24463696 6.40373557 9.22774369a1871.4375 1871.4375 0 0 0 140.04691725 5.34970492 1866.36093723 1866.36093723 0 0 0 140.04691723-5.34970492c2.24727335-2.98310674 4.21612437-6.18497483 6.3937923-9.2178004 30.66633723 30.70611158 55.4360664 65.4791928 72.80769147 103.21549355 11.00766384 23.91457269 46.15860503 21.27949489 52.69160879-4.22606768a371.15156223 371.15156223 0 0 0 11.514792-91.99901164c0-69.36717486-19.13165746-133.82216804-51.75690182-187.92578088-3.42062944-5.66790279-4.76302748-12.26056868-3.76865837-18.80351632a462.20625001 462.20625001 0 0 0 5.36959269-69.943909c-0.00994388-166.08943902-87.32547796-311.81420293-218.6915546-394.09823051zM605.93803103 357.87693858a93.93749974 93.93749974 0 1 1-187.89594924 6.1e-7 93.93749974 93.93749974 0 0 1 187.89594924-6.1e-7z" p-id="5405" data-v-c6073ba8></path><path d="M429.50777625 765.63860547C429.50777625 803.39355007 466.44236686 1000.39046097 512.00932183 1000.39046097c45.56695499 0 82.4922232-197.00623328 82.5015456-234.7518555 0-37.75494459-36.9345906-68.35043303-82.4922232-68.34111062-45.57627738-0.00932239-82.52019037 30.59548842-82.51086798 68.34111062z" p-id="5406" data-v-c6073ba8></path></svg></div></div></div>
    <script src="/blog/assets/js/app.0c46c7d8.js" defer></script><script src="/blog/assets/js/3.5679fe00.js" defer></script><script src="/blog/assets/js/1.8cdd2163.js" defer></script><script src="/blog/assets/js/55.3d2e78cc.js" defer></script>
  </body>
</html>
